#include "win32_printer.h"


using namespace Napi;

Napi::String Method(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();
  return Napi::String::New(env, "mini world");
}

Napi::Array getPrinters(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();
  Napi::Array printers = Napi::Array::New(env);

  DWORD dwReturned = 0;
  PPRINTER_INFO_2 pInfo = NULL;
  if (_getPrinters(NULL, (void**)&pInfo, &dwReturned) && pInfo) {
    printers = Napi::Array::New(env, dwReturned);
    // Review the information from all the printers
    //  returned by EnumPrinters.
    for (unsigned int i=0; i < dwReturned; i++) {
      PRINTER_INFO_2* pThisInfo = pInfo + i;
      Napi::Object prt = Napi::Object::New(env);
      prt.Set("name", Napi::String::New(env, (char16_t *)pThisInfo->pPrinterName));
      prt.Set("portName", Napi::String::New(env, (char16_t *)pThisInfo->pPortName));
      prt.Set("driverName", Napi::String::New(env, (char16_t *)pThisInfo->pDriverName));
      // prt.Set("serverName", Napi::String::New(env, pThisInfo->pServerName));
      prt.Set("printProcessor", Napi::String::New(env, (char16_t *)pThisInfo->pPrintProcessor));
      prt.Set("attributes", Napi::Number::New(env, pThisInfo->Attributes));
      prt.Set("offline", Napi::Boolean::New(env, pThisInfo->Attributes & PRINTER_ATTRIBUTE_WORK_OFFLINE));
      prt.Set("status", Napi::Number::New(env, pThisInfo->Status));
      prt.Set("cJobs", Napi::Number::New(env, pThisInfo->cJobs));

      // global options
      Napi::Object globalOptions = _getGlobalDefaultOptions(env, pThisInfo->pDevMode);
      prt.Set("globalOptions", globalOptions);
      // user options
      // Napi::Value userOptions = _getUserDefaultOptions(env, pThisInfo->pPrinterName);
      // prt.Set("userOptions", userOptions);

      printers[i] = prt;
      
    }

    // free memory
    HeapFree(GetProcessHeap(), 0L, pInfo);
  }

  return printers;
}

Napi::Value getUserDefaultOptions(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

  if (info.Length() < 1) {
    Napi::TypeError::New(env, "Wrong number of arguments")
        .ThrowAsJavaScriptException();
    return env.Null();
  }
  std::string arg1 = info[0].As<Napi::String>().Utf8Value();
  std::string printerName(
    utf8ToGBK(const_cast<char*>(arg1.c_str()))
  );
  // Napi::String::Utf8Value *printerName = info[0].As<Napi::String>().Utf8Value();
  // debug
  std::cout << "printerName: " << printerName << std::endl;

  Napi::Value userOptions = _getUserDefaultOptions(env, printerName);
  return userOptions;
}

Napi::Value getJobs(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

  if (info.Length() < 1) {
    Napi::TypeError::New(env, "Wrong number of arguments")
        .ThrowAsJavaScriptException();
    return env.Null();
  }
  std::string arg1 = info[0].As<Napi::String>().Utf8Value();
  std::string printerName(
    utf8ToGBK(const_cast<char*>(arg1.c_str()))
  );
  Napi::Array jobs = _getPrinterJobs(env, printerName);
  return jobs;
}

Napi::Value printImg(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

  if (info.Length() < 2) {
    Napi::TypeError::New(env, "Wrong number of arguments")
        .ThrowAsJavaScriptException();
    return env.Null();
  }

  /** 
   * params
   * filename     String
   * printerName  String 
   * options      Object {dpi, orientation, copies, fit}
   */
  std::string arg1 = info[0].As<Napi::String>().Utf8Value();
  std::string filename(
    utf8ToGBK(const_cast<char*>(arg1.c_str()))
  );
  std::string arg2 = info[1].As<Napi::String>().Utf8Value();
  std::string printerName(
    utf8ToGBK(const_cast<char*>(arg2.c_str()))
  );
  Napi::Object options = Napi::Object::New(env);
  if (info.Length() >= 3) {
    options = info[2].As<Napi::Object>();
  }

  DWORD jobId = _printImg(filename, printerName, options);
  if (jobId) {
    return Napi::Number::New(env, jobId);
  }
  return env.Null();
}

// get job info
Napi::Value getJob(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

  if (info.Length() < 2) {
    Napi::TypeError::New(env, "Wrong number of arguments")
        .ThrowAsJavaScriptException();
    return env.Null();
  }

  std::string arg1 = info[0].As<Napi::String>().Utf8Value();
  std::string printerName(
    utf8ToGBK(const_cast<char*>(arg1.c_str()))
  );
  int jobId = info[1].As<Napi::Number>().Uint32Value();

  Napi::Value job = _getJob(env, printerName, jobId);
  return job;
}

Napi::Boolean cancel(const Napi::CallbackInfo& info) {
  Napi::Env env = info.Env();

  if (info.Length() < 2) {
    Napi::TypeError::New(env, "Wrong number of arguments")
        .ThrowAsJavaScriptException();
    return Napi::Boolean::New(env, false);
  }

  std::string arg1 = info[0].As<Napi::String>().Utf8Value();
  std::string printerName(
    utf8ToGBK(const_cast<char*>(arg1.c_str()))
  );
  int jobId = info[1].As<Napi::Number>().Uint32Value();

  BOOL fnReturn = _setJob(env, printerName, jobId, JOB_CONTROL_DELETE);
  return Napi::Boolean::New(env, fnReturn);
}


Napi::Object Init(Napi::Env env, Napi::Object exports) {
  exports.Set(Napi::String::New(env, "Win32Printer"),
              Napi::Function::New(env, Method));
  // get printers
  exports.Set(Napi::String::New(env, "getPrinters"),
              Napi::Function::New(env, getPrinters));

  // get user options
  exports.Set(Napi::String::New(env, "getUserDefaultOptions"),
              Napi::Function::New(env, getUserDefaultOptions));

  // get jobs
  exports.Set(Napi::String::New(env, "getJobs"),
              Napi::Function::New(env, getJobs));

  // get single job
  exports.Set(Napi::String::New(env, "getJob"),
              Napi::Function::New(env, getJob));

  // print doc
  exports.Set(Napi::String::New(env, "printImg"),
              Napi::Function::New(env, printImg));

  return exports;
}

NODE_API_MODULE(addon, Init)
